from logging import getLogger
import subprocess
import sys

__all__ = ['shell', 'shell_array']

class ShellCommandFailedError(Exception):
	"""raised whenever a shell command fails"""
	
	stdout = property(lambda self: self.__stdout)
	stderr = property(lambda self: self.__stderr)
	returncode = property(lambda self: self.__returncode)
	
	# TODO: deprecate
	retcode = property(lambda self: self.__returncode)
	
	def __init__(self, cmd, stdout, stderr, returncode):
		Exception.__init__(self, "command '%s' failed with status %s" % (cmd, returncode))
		self.__returncode = returncode
		self.__stdout = stdout
		self.__stderr = stderr

class Result(object):
	
	returncode = property(lambda self: self.__returncode)
	stdout = property(lambda self: self.__stdout)
	stderr = property(lambda self: self.__stderr)
	
	def __init__(self, stdout, stderr, returncode):
		self.__returncode = returncode
		self.__stdout = stdout
		self.__stderr = stderr

def shell(cmd, quiet=False, allow_failure=False):
	"""runs a shell command, echoing output and raising
	a SystemExit if the command fails"""
	logger = getLogger("metamake.shell")
	if not quiet:
		logger.info("\n Calling: " + cmd + "\n")
	
	pipe = subprocess.Popen(cmd.split(' '), shell=False, stderr=subprocess.PIPE)
	complete = False
	while True:
		out = pipe.stderr.read(1)
		if out == '' and pipe.poll() != None:
				break
		if out != '':
				sys.stdout.write(out)
				sys.stdout.flush()
	
	#pipe.wait()
	returncode = pipe.returncode
	if returncode is not 0 and not allow_failure:
		raise ShellCommandFailedError(cmd, stdout=pipe.stdout, stderr=pipe.stderr, returncode=pipe.returncode)
	return Result(stdout=pipe.stdout, stderr=pipe.stderr, returncode=pipe.returncode)

def shell_array(cmd, quiet=False, allow_failure=False):
	"""Similar to the shell function, however the command must be a list. This is meant to handle filenames or arguments with spaces. If there is any doubt, use this."""
	logger = getLogger("metamake.shell")
	if not quiet:
		logger.info("\n Calling: " + ' '.join(cmd) + "\n")
	
	pipe = subprocess.Popen(cmd, shell=False, stderr=subprocess.PIPE)
	complete = False
	while True:
		out = pipe.stderr.read(1)
		if out == '' and pipe.poll() != None:
				break
		if out != '':
				sys.stdout.write(out)
				sys.stdout.flush()
	
	#pipe.wait()
	returncode = pipe.returncode
	if returncode is not 0 and not allow_failure:
		raise ShellCommandFailedError(cmd, stdout=pipe.stdout, stderr=pipe.stderr, returncode=pipe.returncode)
	return Result(stdout=pipe.stdout, stderr=pipe.stderr, returncode=pipe.returncode)

